using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FloralTerrainGenerator.Source;
using System.IO;

namespace FloralTerrainGenerator {
	/// <summary>
	/// This is the main type for your game
	/// </summary>
	public class FloralTerrainGenerator:Microsoft.Xna.Framework.Game {
		GraphicsDeviceManager graphics;
		SpriteBatch spriteBatch;
		public static GraphicsDevice gd;
		public static ContentManager sContent;

		public static Matrix viewMatrix;

		public static Vector3 cPos;
		public static float crX, crY, crZ;
		Boolean cA;

		Terrain terrain;

		Boolean walk = true;
		Boolean oldWalk = false;

        Boolean newF;
        Boolean oldF;

		SpriteFont debugFont;
		int fps = 0;

		public FloralTerrainGenerator() {
			graphics = new GraphicsDeviceManager(this);
			Content.RootDirectory = "Content";
		}

		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize() {
			// TODO: Add your initialization logic here
            graphics.PreferredBackBufferHeight = 768;
            graphics.PreferredBackBufferWidth = 1024;
            graphics.ApplyChanges();
			sContent = Content;
			base.Initialize();
		}

		/// <summary>
		/// LoadContent will be called once per game and is the place to load
		/// all of your content.
		/// </summary>
		protected override void LoadContent() {
			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);
			gd = GraphicsDevice;

			debugFont = Content.Load<SpriteFont>("debugFont");

			cPos = new Vector3(0,-450,0);

			crX = (float) Math.PI/3;
			crY = 0;
			crZ = 0;

			viewMatrix = Matrix.Identity;
			viewMatrix *= Matrix.CreateTranslation(cPos);
			viewMatrix *= Matrix.CreateRotationX(crX);
			viewMatrix *= Matrix.CreateRotationY(crY);
			viewMatrix *= Matrix.CreateRotationZ(crZ);

			terrain = new Terrain(2000, 2000, 300, 300, 0, 600, 0, 50, 200, 400, "heightmaps//leaf");
			terrain.isWireFrame(false);
			terrain.setTextureCoords(32);

			Texture2D grass = Content.Load<Texture2D>("textures//Grass0027_13_thumbhuge");
			Texture2D snow = Content.Load<Texture2D>("textures//GroundFrozen0039_3_thumbhuge");
			Texture2D mud = Content.Load<Texture2D>("textures//mud");
			Texture2D rocky = Content.Load<Texture2D>("textures//RockGrassy0030_5_thumbhuge");

			terrain.setTextures(mud, grass, grass, snow);

			terrain.addWater(grass, 10, 1f, new Vector4(0, 0, 1, 1));

			List<Vector2> plantsList = new List<Vector2>();
			plantsList.Add(new Vector2(25,25));
			plantsList.Add(new Vector2(26,26));
			plantsList.Add(new Vector2(25,26));
			plantsList.Add(new Vector2(26,25));
			plantsList.Add(new Vector2(28,25));
			plantsList.Add(new Vector2(30,25));
			plantsList.Add(new Vector2(29,26));
			plantsList.Add(new Vector2(29,27));

			float pScale = 2;
			Plant p = terrain.makePlant("models//mangrove",1);
			p.scale = pScale*2;
			terrain.biome0.floraList.Add(p);
			p = terrain.makePlant("models//blackSpruce",10);
			p.scale = pScale*2;
			terrain.biome0.floraList.Add(p);
			p = terrain.makePlant("models//cypress",1);
			p.scale = pScale*2;
			terrain.biome0.floraList.Add(p);

			p = terrain.makePlant("models//oak",1);
			p.scale = pScale;
			terrain.biome1.floraList.Add(p);
			p = terrain.makePlant("models//walnut",1);
			p.scale = pScale;
			terrain.biome1.floraList.Add(p);

			p = terrain.makePlant("models//fitzroya",1);
			p.scale = pScale;
			terrain.biome2.floraList.Add(p);
			p = terrain.makePlant("models//douglasFir",3);
			p.scale = pScale;
			terrain.biome2.floraList.Add(p);

			//p = terrain.makePlant("models//douglasFir",1);
			//p.scale = pScale;
			//terrain.biome3.floraList.Add(p);

			terrain.biome0.biomeDensity = 1f;
			terrain.biome1.biomeDensity = 0.5f;
			terrain.biome2.biomeDensity = 0.3f;
			terrain.biome3.biomeDensity = 0.1f;

			//terrain.initFlora(50);
			//terrain.seedFlora(plantsList);
			Texture2D cM = Content.Load<Texture2D>("conwaymaps//test");
			terrain.initFlora(cM);
			terrain.conwayPopulate(100);
		}

		public static void setViewMatrix(Matrix vM) {
			viewMatrix = vM;
		}

		/// <summary>
		/// UnloadContent will be called once per game and is the place to unload
		/// all content.
		/// </summary>
		protected override void UnloadContent() {
			// TODO: Unload any non ContentManager content here
		}

		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update(GameTime gameTime) {
			//calculate fps
			fps = (int)(1/gameTime.ElapsedGameTime.TotalSeconds);

			//keyboard input
			KeyboardState kS = Keyboard.GetState();
			Vector3 addVec = Vector3.Zero;
			if(kS.IsKeyDown(Keys.W)) {
				addVec = new Vector3((float)Math.Sin(-crY),(float)Math.Sin(crX),(float)Math.Cos(-crY));
			} 
			if(kS.IsKeyDown(Keys.S)) {
				addVec = new Vector3((float)Math.Sin(-crY),(float)Math.Sin(crX),(float)Math.Cos(-crY));
				addVec *= -1;
			} 
			if(kS.IsKeyDown(Keys.A)) {
				addVec = new Vector3((float)Math.Cos(crY), 0,(float)Math.Sin(crY));
			} 
			if(kS.IsKeyDown(Keys.D)) {
				addVec = new Vector3((float)Math.Cos(crY),0,(float)Math.Sin(crY));
				addVec *= -1;
			} 
			if(kS.IsKeyDown(Keys.Q)) {
				cPos.Y -= 0.5f;
			} 
			if(kS.IsKeyDown(Keys.E)) {
				cPos.Y += 0.5f;
			}

			if(kS.IsKeyDown(Keys.Space)) {
				terrain.isWireFrame(true);
			} else {
				terrain.isWireFrame(false);
			}

            oldF = newF;
            newF = kS.IsKeyDown(Keys.F);

            if(newF && !oldF) {
                Console.WriteLine("Saving ... ");
                TextWriter tw = new StreamWriter("terrain.txt");
                tw.WriteLine(terrain.floraMan.Count);
                foreach(Plant p in terrain.floraMan) {
                    tw.WriteLine(p.toString());
                }
                tw.WriteLine();
                tw.WriteLine(terrain.vertices.Length);
                foreach (VertexMultiTex v in terrain.vertices) {
                    tw.WriteLine(v.toString());
                }
                tw.Close();
                Console.WriteLine("Saved.");
            }

			//addVec.Normalize();
			addVec.X *= (float) (20*gameTime.ElapsedRealTime.TotalSeconds);
			addVec.Y *= (float) (20*gameTime.ElapsedRealTime.TotalSeconds);
			addVec.Z *= (float) (20*gameTime.ElapsedRealTime.TotalSeconds);
			cPos += addVec;

			MouseState mS = Mouse.GetState();
			Vector2 c = new Vector2(GraphicsDeviceManager.DefaultBackBufferWidth/2,GraphicsDeviceManager.DefaultBackBufferHeight/2);

			if(cA) {
				crY += (mS.X-c.X) * 0.005f;
				crX += (mS.Y-c.Y) * 0.005f;
			}

			//Mouse.SetPosition((int)c.X,(int)c.Y);
			//Vector2 c = new Vector2(mS.X,mS.Y);

			if(mS.LeftButton == ButtonState.Pressed) {
				Mouse.SetPosition((int)c.X,(int)c.Y);
				cA = true;
			} else {
				cA = false;
			}

			viewMatrix = Matrix.Identity;
			viewMatrix *= Matrix.CreateTranslation(cPos);
			viewMatrix *= Matrix.CreateRotationY(crY);
			viewMatrix *= Matrix.CreateRotationX(crX);
			viewMatrix *= Matrix.CreateRotationZ(crZ);

			terrain.Update(gameTime);

			// Allows the game to exit
			if(GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
				this.Exit();

			// TODO: Add your update logic here

			base.Update(gameTime);
		}

		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw(GameTime gameTime) {
            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = false;
            GraphicsDevice.RenderState.AlphaTestEnable = false;
            GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
			GraphicsDevice.Clear(Color.CornflowerBlue);

			spriteBatch.Begin();

			terrain.drawTerrain(spriteBatch);

			terrain.drawPlants(spriteBatch);

            //terrain.drawWater(spriteBatch);

			spriteBatch.DrawString(debugFont,fps.ToString(),new Vector2(10,10),Color.White);
			
			spriteBatch.End();

			base.Draw(gameTime);
		}
	}
}
